home *** CD-ROM | disk | FTP | other *** search
/ Shareware Grab Bag / Shareware Grab Bag.iso / 001 / comzap.arc / COMZAP.ASM next >
Assembly Source File  |  1985-07-08  |  11KB  |  343 lines

  1.     page    60,132
  2.     title    COMZAP.ASM
  3. ;...................
  4. ;Author:  Edward Nisley
  5. ;Entered from July 1985 issue of PC Tech Journal
  6. ;...................
  7. ;Program to force DOS 2.0 and 2.1 to reload COMMAND.COM
  8. ; using the file name specified by SET COMSPEC=
  9. ;Particularly useful with Vdisks and non-bootable
  10. ; fixed disks.
  11. ;
  12. ;The DOS 2.0 and 2.1 SET COMSPEC= command does not work as
  13. ; documented.  DOS will always attempt to reload COMMAND.COM
  14. ; from the root directory of the disk drive used during
  15. ; system initialization (the boot disk).
  16. ;
  17. ;This utility finds the string A:\COMMAND.COM in DOS
  18. ; and replaces it with the file name set by SET COMSPEC=
  19. ;
  20. ;How it's done:
  21. ; the BIOS memory-size interrupt determines the
  22. ; range of RAM searched for the string.
  23. ;The string A:\COMMAND.COM always starts at an
  24. ; offset address xxx9, with a binary zero byte at
  25. ; offset xxx8 just preceding it.  These observations
  26. ; are used to distinguish the true DOS file name from
  27. ; the string elsewhere (as in the program.COM file
  28. ; a VDISK or the program as loaded in RAM)
  29. ;[Or from same in the keyboard buffer under DEBUG.]
  30. ;Offset 2C in the PSP contains the segment address of
  31. ; the environment area passed to this program.  The
  32. ; COMSPEC= string is located within the envrionment.
  33. ;The COMSPEC= string is copied to the DOS string area
  34. ; and displayed on the screen as it is being copied.
  35. ;If the COMSPEC= string is too long, your PC is dead!
  36. ;The string is truncated to fit the largest space
  37. ; available, which means that it cannot be used to
  38. ; reload the command interprteer.  Power-off time!
  39. ;
  40. ;Assumptions:
  41. ;-DOS 2.0 or DOS 2.1
  42. ;-Power-on boot drive is "A"
  43. ;-You have enough RAM in your system that the
  44. ; string is not in the last segment.  The last segment
  45. ; is not checked, except in 64K systems.
  46. ;-The SET COMSPEC string is not too long.  The maximum
  47. ; length is one less than the number defined in cspecmax.
  48. ;
  49. ;Exit:  DOS function 4CH is used to pass a return code
  50. ; code 0=success!
  51. ; code 1=DOS file name string wasn't found
  52. ; code 2=COMSPEC string wasn't found
  53. ; code 3=COMSPEC string was too long, truncated
  54. ;
  55. page
  56. ;...........................
  57. ;hocus pocus to start up the assembler
  58. ; and get the addresses correct for a .COM file
  59. comseg    segment    'codeseg'
  60.     assume    cs:comseg,ds:comseg,ss:comseg,es:comseg
  61. comzap    proc
  62.     org    100H
  63. start:    jmp    around
  64. ;.........................
  65. ;The file name we are trying to find.  The leading
  66. ; 0FFH byte ensures that this file name isn't preceded
  67. ; by a binary zero.  This prevents incorrect matching
  68. ; when Murphy puts it at the correct offset.
  69.     db    0FFH
  70. bootID    db    'A'        ;the normal boot disk
  71. CCstr    db    ':\COMMAND.COM'    ;last part of string
  72. CCstr_l    equ    $-CCstr        ;string length
  73. ;.........................
  74. ;The COMSPEC= string header in the environment area
  75. specstr    db    'COMSPEC='
  76. specstr_l    equ    $-specstr    ;string length
  77. ;.........................
  78. ;Various & sundry constants
  79. FN_hexit    dw    0009H    ;file name offset hexit
  80. env_seg_at    equ    002CH    ;address of environment segment
  81. prt_chr    equ    02H        ;DOS single-character output
  82. prt_str    equ    09H        ;DOS print-string function
  83. quit    equ    4CH        ;DOS termination function
  84. mem_sz    equ    12H        ;BIOS memory-size interrupt
  85. dosint    equ    21H        ;DOS function interrupt
  86. cr    equ    0DH        ;useful characters
  87. lf    equ    0Ah
  88. term    equ    '$'        ;the prt_str terminator
  89. ;.....................
  90. ;A macro to simplify the DOS interface
  91. DOScall    macro    DOS_fn
  92.     mov    ah,DOS_fn
  93.     int    dosint
  94.     endm
  95. page
  96. ;.......................
  97. ;Variables
  98. FN_at    label    dword        ;seg:offset address of
  99. FN_off    dw    0        ;DOS file name string
  100. FN_seg    dw    0
  101. cspec_at    label    dword    ;seg:offset of COMSPEC=
  102. cspec_off    dw    0    ;string in enfironment
  103. cspec_seg    dw    0
  104. cspecmax    equ    39    ;longest string allowed
  105.                 ; includes trailing zero
  106. last_seg    dw    0    ;last segment to check
  107. ;.......................
  108. ;Messages
  109. Msg0    db    'COMZAP: a DOS fixit utility',cr,lf
  110.     db    'Revised 4 April 85',cr,lf
  111.     db    cr,lf
  112.     db    'Searching for A:\COMMAND.COM...'
  113. CrLf    db    cr,lf,term
  114. Msg1    db    ' >> not found.',cr,lf
  115.     db    ' >> Remember that COMZAP.COM can'
  116.     db    cr,lf
  117.     db    ' >> be run only once per cold boot'
  118.     db    cr,lf,term
  119. Msg2    db    cr,lf
  120.     db    'The DOS command interpreter will be'
  121.     db    'reloaded from',cr,lf
  122.     db    '   ',term
  123. Msg3    db    'Searching for COMSPEC= string...'
  124.     db    cr,lf,term
  125. Msg4    db    ' >> not found.',cr,lf
  126.     db    ' >> Did you use SET COMSPEC=filespec'
  127.     db    cr,lf
  128.     db    ' >> before running COMZAP.COM?'
  129.     db    cr,lf,term
  130. Msg5    db    cr,lf
  131.     db    ' >> WARNING:  COMSPEC string too long,'
  132.     db    cr,lf
  133.     db    ' >> DOS will not reload the interpreter'
  134. db    cr,lf,term
  135. page
  136. ;........................
  137. ;Start of the program code
  138. ;
  139. ;Show some identification
  140. around:    cld            ;set increment mode
  141.     mov    dx,offset Msg0    ;show ID message
  142.     DOScall    prt_str
  143. ;.........................
  144. ;Get storage size and convert to segment address.
  145. ;Because the last segment may not be full, we will not
  146. ; test it for the string.
  147. ;Because we are testing in units of 64K (full segments)
  148. ; only the high-order hexit of the segment address
  149. ; is useful in comparisons.
  150. ;64K PC's (are there any left?) are special-cases.
  151.     int    mem_sz        ;storage in 1K units
  152.     mov    cl,6        ;shift MSB to bit 15
  153.     shl    ax,cl
  154.     and    ax,0F000H    ;isolate 64K seg hexit
  155.     jz    oneseg        ;handle 64K machines
  156.     sub    ax,01000H    ;set last segment
  157. oneseg:    mov    last_seg,ax
  158. ;........................
  159. ;Search for the starting "A" in "A:\COMMAND.COM"
  160. ;The inner loop uses CMP to check the 4096 offset
  161. ; addresses ending in the hexit 9 in each segment.
  162. ;The outer loop verifies inner-loop hits and ticks
  163. ; the segment addresses at the end of segments
  164.     mov    al,bootID    ;the test byte
  165.     mov    bx,00000H    ;first segment address
  166.     mov    es,bx        ;into segment register
  167.     assume    es:nothing    ;tell the assembler
  168. CClp0:    mov    si,FN_hexit    ;get the offset address
  169.     sub    si,0010h    ;fix for pre-increment
  170.     mov    cx,1000H    ;64K/16 = # of 9's
  171. CClp1:    add    si,0010H    ;tick offset pointer
  172.     cmp    al,es:[si]    ;test the byte
  173.     loopne    CClp1        ;loop if not equal
  174. ;.....................
  175. ;Inner loop ended:
  176. ; if "not equal", the entire segment has been examined
  177. ; without finding a qualifying "A", so go to next seg
  178.     jne    nextseg
  179. ;......................
  180. ;We've found an "A" at an offset xxx9
  181. ; if it's preceded by a binary zero, look closer...
  182.     cmp    byte ptr es:[si-1],00H
  183.     je    testmore    ;hmm... looking good!
  184. page
  185. ;.......................
  186. ;The "A" doesn't pass muster, so continue with the
  187. ; next byte in the segment.
  188. ;If CX is zero, all bytes have been tested,
  189. ; so we must step to the next segment
  190.     cmp    cx,0
  191.     jne    CClp1
  192.     jmp    nextseg
  193. ;......................
  194. ;An "A" and a binary zero have been located!!!
  195. ;Time to compare the strings.
  196. ;Save the current state, just in case it's a miss
  197. testmore:
  198.     push    si        ;save current offest
  199.     push    es        ;  ... segment
  200.     push    cx        ;  ... count
  201.     push    ax        ;  ... test byte
  202. ;.................
  203. ;Set the segment address to the paragraph just before
  204. ; the string to avoid overrunning the segment boundary
  205. ;This is complicated a bit by the restricted nature of
  206. ; the 8088's "general" registers
  207.     mov    bx,si        ;get current offset
  208.     mov    cl,4        ;convert to paragraphs
  209.     shr    bx,cl
  210.     mov    cx,es        ;add the segment
  211.     add    bx,cx
  212.     mov    es,bx        ;set in in seg reg
  213.     mov    FN_seg,bx    ;save segment
  214. ;.....................
  215. ;Set up the offset addresses to the known string and
  216. ; the candidate we just found
  217. ;The candidate offset is always FN_hexit by definition
  218.     mov    di,FN_hexit    ;known candidate offset
  219.     mov    FN_off,di    ;save offset
  220.     inc    di        ;step over "A"
  221.     mov    si,offset CCstr    ;known string
  222.     mov    cx,CCstr_l    ;  ... its length
  223. ;....................
  224. ;After all that, this is the string com0parison.
  225. ;Registers are restored before the branch simply to
  226. ; avoid a double branch.
  227. ;The CMPS operands are coded for human readability.
  228. ;Failure returns us to the inner test loop above.
  229. ;Success passes us to the COMSPEC= string eaarch.
  230.     repe    cmps    byte ptr ds:[si],es:[di]
  231.     pop    ax        ;restore saved regs
  232.     pop    cx
  233.     pop    es
  234.     pop    si
  235.     jne    CClp1        ;no good, continue
  236.     jmp    gotfile        ;swell, break out
  237. page
  238. ;.....................
  239. ;outer loop ending...
  240. ;Didn't find the string in this segment
  241. ;Trick the segment until we've completed the search.
  242. ;Remember that the RAM in the last segment will not
  243. ; be examined because the segment may not be filled
  244. ; with RAM...
  245. ;A tige more code would make the search complete.
  246. nextseg:
  247.     mov    bx,es        ;tick the segment pointer
  248.     add    bx,1000H
  249.     mov    es,bx
  250.     cmp    bx,last_seg    ;done?
  251.     jbe    CClp0        ;nope, back to loops
  252.     mov    dx,offset Msg1    ;present error message
  253.     DOScall    prt_str
  254.     mov    al,1        ;return code
  255.     DOScall    quit
  256. page
  257. ;......................
  258. ;Tah-DAH!  Found the DOS file specifier in RAM.
  259. ;Now we have to look for the COMSPEC string.
  260. ;Each string in the environment is terminated with a
  261. ; binary zero.  The end of the environment area is
  262. ; marked by two binary zeros.
  263. ;Note how the "general" pointer registers aren't...
  264. gotfile:
  265.     mov    dx,offset Msg3    ;say where we are
  266.     DOScall    prt_str
  267.     mov    si,env_seg_at    ;points to env seg ptr
  268.     mov    es,[si]        ;pick up segment addr
  269.     mov    cspec_seg,es    ;save seg addr
  270. findspec:
  271.     mov    di,cspec_off    ;get offs addr
  272.     cmp    byte ptr es:[di],00h    ;at end?
  273.     je    nospec        ;sigh, give up
  274.     mov    si,offset specstr    ;test string
  275.     mov    cx,specstr_l
  276.     repe    cmps    byte ptr ds:[si],es:[di]
  277.     je    gotspec        ;kapow!
  278.     mov    si,di        ;use right "general" reg
  279. junkit:    lods    byte ptr es:[si]    ;scan for end
  280.     cmp    al,00H
  281.     jne    junkit
  282.     mov    cspec_off,si    ;set up pointer
  283.     jmp    findspec    ;and try again
  284. ;..........................
  285. ;Didn't find a COMSPEC= string in the environment.
  286. ;Return to DOS with an error code.
  287. nospec:
  288.     mov    dx,offset Msg4    ;show a sign
  289.     DOScall    prt_str
  290.     mov    al,2        ;set return code
  291.     DOScall    quit
  292. page
  293. ;......................
  294. ;Found the COMSPEC= string in the enfironment area
  295. ;ES:DI now points to the start of the file spec string
  296. ;Need to get source in DS:SI and target in ES:DI
  297. ;Transfer the COMSPEC= string to the DOS file string
  298. ; and display it as it is transferred.
  299. ;The trailing binary zero is transferred as well, to
  300. ; overwrite any previous file name you may have
  301. ; manually inserted into the DOS file string while
  302. ; debugging this program...
  303. ;The string will be truncated to prevent stamping on
  304. ; other DOS data following the file name area.
  305. gotspec:
  306.     mov    cspec_off,di    ;save offset
  307.     mov    dx,offset Msg2    ;say what's happening
  308.     DOScall    prt_str
  309.     mov    cx,cspecmax    ;max length allowed
  310.     les    di,FN_at    ;DOS string address
  311.     lds    si,cspec_at    ;COMSPEC string addr
  312. xfer:    lods    byte ptr ds:[si]    ;COMSPEC char
  313.     stos    byte ptr es:[di]    ;to DOS char
  314.     cmp    al,00H        ;hit the end?
  315.     je    alldone        ;yup, quit
  316.     mov    dl,al        ;nope, show it
  317.     DOScall    prt_chr
  318.     loop    xfer        ;repeat for count
  319.                 ;if fall through, error!
  320.     mov    ax,cs        ;restore DS
  321.     mov    ds,ax
  322.     mov    dx,offset Msg5    ;tell them about truncation
  323.     DOScall    prt_str
  324.     mov    al,3        ;set return code
  325.     DOScall    quit
  326. ;..................
  327. ;Done with transfer and display, return to DOS
  328. alldone:
  329.     mov    ax,cs        ;restore DS
  330.     mov    ds,ax
  331.     mov    dx,offset crlf    ;insert some space
  332.     DOScall    prt_str
  333.     mov    al,0        ;good return code
  334.     DOScall    quit        ;and exit
  335. page
  336. ;....................
  337. ;hocus pocus to turn off the assembler
  338. ; and set up .COM starting address
  339. comzap    endp
  340. comseg    ends
  341. end    start
  342.  
  343.